using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework;
using GameFramework.Event;
using UnityGameFramework.Runtime;
using LS.Event;
using LS.Saving;
using LS.UnityExtensions;

namespace LS
{

    public enum SavingPathType
    {
        Undefined,
        PersistentData,
        TemporaryCache,
        Data
    }

    [DisallowMultipleComponent]
    [AddComponentMenu("Game Framework/Saving")]
    public sealed partial class SavingComponent : GameFrameworkComponent
    {
        [SerializeField]
        private SavingPathType m_SavingPathType = SavingPathType.PersistentData;

        // [SerializeField]
        // private bool m_PeriodicAutoSaving = false;
        //
        // [SerializeField]
        // private float m_AutoSavingInterval = 60f;

        [SerializeField]
        [TypeSearch(typeof(SaveHelperBase), "SavingHelperType")]
        private string m_SavingHelperTypeName = "LS.Saving.DefaultSavingHelper";

        [SerializeField]
        private SaveHelperBase m_DefaultSaveHelper = null;

        [SerializeField]
        private Transform m_InstanceRoot = null;

        [SerializeField]
        private SavingGroup[] m_SavingGroups = null;

        private SavingManager m_SaveManager = null;
        
        private EventComponent m_EventComponent = null;

        private float m_Timer = 0;

        #region Monobehaviour Lifecycle

        protected override void Awake()
        {
            base.Awake();
            if (m_InstanceRoot == null)
            {
                m_InstanceRoot = this.transform;
            }

            m_SaveManager = new SavingManager();
            SaveHelperBase savingHelper = Helper.CreateHelper(m_SavingHelperTypeName, m_DefaultSaveHelper);
            if (savingHelper == null)
            {
                Log.Error("Can not create saving helper.");
                return;
            }

            savingHelper.name = "Saving.Helper";
            Transform transform = savingHelper.transform;
            transform.SetParent(this.transform);
            transform.localScale = Vector3.one;

            m_SaveManager.SetHelper(savingHelper);

            for (int i = 0; i < m_SavingGroups.Length; i++)
            {
                var groupInfo = m_SavingGroups[i];
                AddSavingGroup(groupInfo.Name, groupInfo.FolderName, groupInfo.FileExtension);
            }

            m_SaveManager.BeginSave += BeginSaveEventHandler;
            m_SaveManager.EndSave += EndSaveEventHandler;
            m_SaveManager.BeginSaveDatabase += BeginSaveDatabaseEventHandler;
            m_SaveManager.EndSaveDatabase += EndSaveDatabaseEventHandler;
            m_SaveManager.BeginLoad += BeginLoadEventHandler;
            m_SaveManager.EndLoad += EndLoadEventHandler;
            m_SaveManager.BeginLoadDatabase += BeginLoadDatabaseEventHandler;
            m_SaveManager.EndLoadDatabase += EndLoadDatabaseEventHandler;

        }

        private void Start()
        {
            m_EventComponent = UnityGameFramework.Runtime.GameEntry.GetComponent<EventComponent>();
        }

        // private void Update()
        // {
        //     if (m_PeriodicAutoSaving)
        //     {
        //         m_Timer += Time.unscaledDeltaTime;
        //         if (Time.unscaledTime - m_Timer > m_AutoSavingInterval)
        //         {
        //             m_Timer = Time.unscaledTime;
        //             Save();
        //         }
        //     }
        // }

        private void OnDestroy()
        {
            m_SaveManager.Shutdown();
        }

        #endregion

        #region Event

        //private void OnApplicationFocusChanged(object sender, GameEventArgs e)
        //{
        //    //ApplicationFocusChangedEventArgs args = e as ApplicationFocusChangedEventArgs;
        //    //if (args == null)
        //    //{
        //    //    return;
        //    //}

        //    //if (!args.Focus)
        //    //{
        //    //    if(m_SaveOnApplicationFocusOut)
        //    //        Save();
        //    //}
        //    //else
        //    //{
        //    //    if (m_LoadOnApplicationFocusIn)
        //    //        Load();
        //    //}
        //}

        #endregion

        #region SavingGroup Function

        public IDataFile GetFile(string groupName, string fileName)
        {
            return m_SaveManager.GetSavingGroup(groupName).GetFile(fileName);
        }
        
        public bool HasFile(string groupName, string fileName)
        {
            return m_SaveManager.HasFile(groupName, fileName);
        }

        public IDataFile[] GetAllFile(string groupName)
        {
            return m_SaveManager.GetSavingGroup(groupName).GetAllFile();
        }

        public IDataFile GetRecentlyFile(string groupName)
        {
            return m_SaveManager.GetSavingGroup(groupName).GetRecentlyFile();
        }
        
        public IDataFile CreateFileInfo(string groupName, string fileName)
        {
            return m_SaveManager.GetSavingGroup(groupName).CreateFileInfo(fileName);
        }

        public bool HasSavingGroup(string groupName)
        {
            return m_SaveManager.HasSavingGroup(groupName);
        }

        public bool AddSavingGroup(string groupName)
        {
            return m_SaveManager.AddSavingGroup(groupName);
        }

        public bool AddSavingGroup(string groupName, string folderName, string extension)
        {
            return m_SaveManager.AddSavingGroup(groupName, folderName, extension);
        }

        public ISavingGroup GetSavingGroup(string groupName)
        {
            return m_SaveManager.GetSavingGroup(groupName);
        }

        public ISavingGroup[] GetAllSavingGroup()
        {
            return m_SaveManager.GetAllSavingGroup();
        }

        public void GetAllSavingGroup(List<ISavingGroup> results)
        {
            m_SaveManager.GetAllSavingGroup(results);
        }

        public Database GetDatabase(string groupName)
        {
            return m_SaveManager.GetDatabase(groupName);
        }

        public void ClearData(string groupName)
        {
            m_SaveManager.ClearData(groupName);
        }

        #endregion

        #region Save Behaviour

        public void Load(string groupName)
        {
            m_SaveManager.Load(groupName);
        }
        public void Load(string groupName, string fileName)
        {
            m_SaveManager.Load(groupName, fileName);
        }
        
        public void Load(string groupName, IDataFile file)
        {
            m_SaveManager.Load(groupName, file);
        }

        public void Save(string groupName, bool createNewFile)
        {
            m_SaveManager.Save(groupName, createNewFile);
        }

        public void Save(string groupName, IDataFile file)
        {
            m_SaveManager.Save(groupName, file);
        }

        public void Save(string groupName, string fileName)
        {
            m_SaveManager.Save(groupName, fileName);
        }
        
        public void SetData<T>(string groupName, string dataName, T value)
        {
            m_SaveManager.SetData<T>(groupName, dataName, value);
        }
        
        public void SetObject(string groupName, string dataName, object value)
        {
            m_SaveManager.SetObject(groupName, dataName, value);
        }
        
        public void SetString(string groupName, string dataName, string value)
        {
            m_SaveManager.SetString(dataName, groupName, value);
        }

        public void SetInt(string groupName, string dataName, int value)
        {
            m_SaveManager.SetInt(groupName, dataName, value);
        }

        public void SetFloat(string groupName, string dataName, float value)
        {
            m_SaveManager.SetFloat(groupName, dataName, value);
        }

        public void SetBool(string groupName, string dataName, bool value)
        {
            m_SaveManager.SetBool(groupName, dataName, value);
        }

        public object GetObject(string groupName, string dataName)
        {
            return m_SaveManager.GetObject(groupName, dataName);
        }
        public T GetData<T>(string groupName, string dataName)
        {
            return m_SaveManager.GetData<T>(groupName, dataName);
        }
        
        public string GetString(string groupName, string dataName)
        {
            return m_SaveManager.GetString(groupName, dataName);
        }

        public int GetInt(string groupName, string dataName)
        {
            return m_SaveManager.GetInt(groupName, dataName);
        }

        public float GetFloat(string groupName, string dataName)
        {
            return m_SaveManager.GetFloat(groupName, dataName);
        }

        public bool GetBool(string groupName, string dataName)
        {
            return m_SaveManager.GetBool(groupName, dataName);
        }
        
        #endregion

        #region Events

        private void BeginSaveEventHandler(object sender, InternalBeginSaveEventArgs args)
        {
            SendBeginSaveEvent();
        }

        private void EndSaveEventHandler(object sender, InternalEndSaveEventArgs args)
        {
            SendEndSaveEvent();
        }

        private void BeginSaveDatabaseEventHandler(object sender, InternalBeginSaveDatabaseEventArgs args)
        {
            SendBeginSaveDatabaseEvent(args.GroupName, args.File, args.Database);
        }

        private void EndSaveDatabaseEventHandler(object sender, InternalEndSaveDatabaseEventArgs args)
        {
            if (args.Success)
            {
                SendSaveDatabaseSuccessEvent(args.GroupName, args.File, args.Database);
            }
            else
            {
                SendSaveDatabaseFailureEvent(args.GroupName, args.File, args.Database, args.ErrorMessage);
            }
        }

        private void BeginLoadEventHandler(object sender, InternalBeginLoadEventArgs args)
        {
            SendBeginLoadEvent();
        }

        private void EndLoadEventHandler(object sender, InternalEndLoadEventArgs args)
        {
            SendEndLoadEvent();
        }

        private void BeginLoadDatabaseEventHandler(object sender, InternalBeginLoadDatabaseEventArgs args)
        {
            SendBeginLoadDatabaseEvent(args.GroupName, args.File, args.Database);
        }

        private void EndLoadDatabaseEventHandler(object sender, InternalEndLoadDatabaseEventArgs args)
        {
            if (args.Success)
            {
                SendLoadDatabaseSuccessEvent(args.GroupName, args.File, args.Database);
            }
            else
            {
                SendLoadDatabaseFailureEvent(args.GroupName, args.File, args.Database, args.ErrorMessage);
            }
        }

        private void SendBeginSaveEvent()
        {
            BeginSaveEventArgs args = BeginSaveEventArgs.Create<BeginSaveEventArgs>();

            m_EventComponent.FireNow(this, args);
        }

        private void SendEndSaveEvent()
        {
            EndSaveEventArgs args = BeginSaveEventArgs.Create<EndSaveEventArgs>();

            m_EventComponent.Fire(this, args);
        }

        private void SendBeginLoadEvent()
        {
            BeginLoadEventArgs args = BeginSaveEventArgs.Create<BeginLoadEventArgs>();

            m_EventComponent.FireNow(this, args);
        }

        private void SendEndLoadEvent()
        {
            EndLoadEventArgs args = BeginSaveEventArgs.Create<EndLoadEventArgs>();

            m_EventComponent.Fire(this, args);
        }

        private void SendBeginLoadDatabaseEvent(string groupName, IDataFile file, Database database)
        {
            BeginLoadDatabaseEventArgs args = BeginSaveEventArgs.Create<BeginLoadDatabaseEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            m_EventComponent.Fire(this, args);
        }

        private void SendLoadDatabaseSuccessEvent(string groupName, IDataFile file, Database database)
        {
            LoadDatabaseSuccessEventArgs args = BeginSaveEventArgs.Create<LoadDatabaseSuccessEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            m_EventComponent.Fire(this, args);
        }

        private void SendLoadDatabaseFailureEvent(string groupName, IDataFile file, Database database, string error)
        {
            LoadDatabaseFailureEventArgs args = BeginSaveEventArgs.Create<LoadDatabaseFailureEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            args.Error = error;
            m_EventComponent.Fire(this, args);
        }

        private void SendBeginSaveDatabaseEvent(string groupName, IDataFile file, Database database)
        {
            BeginSaveDatabaseEventArgs args = BeginSaveEventArgs.Create<BeginSaveDatabaseEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            m_EventComponent.Fire(this, args);
        }

        private void SendSaveDatabaseSuccessEvent(string groupName, IDataFile file, Database database)
        {
            SaveDatabaseSuccessEventArgs args = BeginSaveEventArgs.Create<SaveDatabaseSuccessEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            m_EventComponent.Fire(this, args);
        }

        private void SendSaveDatabaseFailureEvent(string groupName, IDataFile file, Database database, string error)
        {
            SaveDatabaseFailureEventArgs args = BeginSaveEventArgs.Create<SaveDatabaseFailureEventArgs>();
            args.GroupName = groupName;
            args.File = file;
            args.Database = database;
            args.Error = error;
            m_EventComponent.Fire(this, args);
        }

        #endregion
    }
}
